package com.lifeonwalden.fincalc.fund.service;

import com.lifeonwalden.fincalc.bean.DateValuePair;
import com.lifeonwalden.fincalc.fund.bean.FundValueInfo;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;

public interface FundPerformanceProcessor {
    /**
     * 计算日涨跌幅
     *
     * @param dataList  日期升序的日频净值数据
     * @param convertor
     * @param <D>
     * @return
     */
    static <D> List<FundValueInfo> calcChange(List<D> dataList, Function<D, FundValueInfo> convertor) {
        List<FundValueInfo> newList = new ArrayList<>();
        FundValueInfo yesterdayData = convertor.apply(dataList.get(0));
        yesterdayData.setChange(BigDecimal.ZERO);
        newList.add(yesterdayData);

        for (int i = 1; i < dataList.size(); i++) {
            FundValueInfo todayData = convertor.apply(dataList.get(i));
            todayData.setChange(todayData.getAccuNav().subtract(yesterdayData.getAccuNav()).divide(yesterdayData.getNav(), 6, RoundingMode.HALF_UP));
            newList.add(todayData);

            yesterdayData = todayData;
        }

        return newList;
    }

    /**
     * 计算日涨跌幅及归一化复权净值
     *
     * @param dataList  日期升序的日频净值数据
     * @param convertor
     * @param <D>
     * @return
     */
    static <D> List<FundValueInfo> calcChangeAndAdjustNav(List<D> dataList, Function<D, FundValueInfo> convertor) {
        List<FundValueInfo> newList = new ArrayList<>();
        FundValueInfo yesterdayData = convertor.apply(dataList.get(0));
        yesterdayData.setChange(BigDecimal.ZERO).setAdjustNav(BigDecimal.ONE);
        newList.add(yesterdayData);

        for (int i = 1; i < dataList.size(); i++) {
            FundValueInfo todayData = convertor.apply(dataList.get(i));
            todayData.setChange(todayData.getAccuNav().subtract(yesterdayData.getAccuNav()).divide(yesterdayData.getNav(), 6, RoundingMode.HALF_UP));
            todayData.setAdjustNav(yesterdayData.getAdjustNav().multiply(BigDecimal.ONE.add(todayData.getChange())).setScale(4, RoundingMode.HALF_UP));
            newList.add(todayData);

            yesterdayData = todayData;
        }

        return newList;
    }

    /**
     * 计算日涨跌幅、复权净值及归一化复权净值
     *
     * @param dataList  日期升序的日频净值数据
     * @param convertor
     * @param <D>
     * @return
     */
    static <D> List<FundValueInfo> calcChgAdjAndNormalAdjustNav(List<D> dataList, Function<D, FundValueInfo> convertor) {
        List<FundValueInfo> newList = new ArrayList<>();
        FundValueInfo yesterdayData = convertor.apply(dataList.get(0));
        yesterdayData.setChange(BigDecimal.ZERO).setAdjustNav(BigDecimal.ONE).setActualAdjustNav(yesterdayData.getNav());
        newList.add(yesterdayData);

        for (int i = 1; i < dataList.size(); i++) {
            FundValueInfo todayData = convertor.apply(dataList.get(i));
            todayData.setChange(todayData.getAccuNav().subtract(yesterdayData.getAccuNav()).divide(yesterdayData.getNav(), 6, RoundingMode.HALF_UP));
            todayData.setAdjustNav(yesterdayData.getAdjustNav().multiply(BigDecimal.ONE.add(todayData.getChange())).setScale(4, RoundingMode.HALF_UP));
            todayData.setActualAdjustNav(yesterdayData.getActualAdjustNav().multiply(BigDecimal.ONE.add(todayData.getChange())).setScale(4, RoundingMode.HALF_UP));
            newList.add(todayData);

            yesterdayData = todayData;
        }

        return newList;
    }

    /**
     * 计算指定周期涨跌幅
     *
     * @param dataList   日期升序的日频净值数据
     * @param convertor
     * @param periodDays 区间天数
     * @param <D>
     * @return
     */
    static <D> Optional<List<DateValuePair>> calcPeriodChange(List<D> dataList, Function<D, FundValueInfo> convertor, int periodDays) {
        int size = dataList.size();
        if (periodDays >= size) {
            return Optional.empty();
        }

        List<DateValuePair> chgList = new ArrayList<>();
        int startIdx = size - ((size - 1) / periodDays) * periodDays - 1;
        FundValueInfo previousPoint = convertor.apply(dataList.get(startIdx));
        for (int i = startIdx + 7; i < size; i += 7) {
            FundValueInfo point = convertor.apply(dataList.get(i));
            chgList.add(new DateValuePair().setDate(point.getDate()).setValue(point.getAccuNav().subtract(previousPoint.getAccuNav()).divide(previousPoint.getNav(), 6, RoundingMode.HALF_UP)));

            previousPoint = point;
        }

        return Optional.of(chgList);
    }
}
